/*----------------------------------------------------------------------------

	Copyright (c) 2006, Creator of the Cant Project.
	All rights reserved.

	Redistribution and use in source and binary forms, with or without
	modification, are permitted provided that the following conditions are met:

	+ Redistributions of source code must retain the above copyright notice,
	this list of conditions and the following disclaimer.

	+ Redistributions in binary form must reproduce the above copyright notice,
	this list of conditions and the following disclaimer in the documentation
	and/or other materials provided with the distribution.

	+ Neither the name of the Creator of the Cant Project nor the names of its
	contributors may be used to endorse or promote products derived from this
	software without specific prior written permission.

	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
	AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
	ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
	LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
	CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
	POSSIBILITY OF SUCH DAMAGE.

----------------------------------------------------------------------------*/

#include	"projtag.hpp"

#include	"tags/filetag.hpp"



/**
	Default constructor.
	
	Required for STL containers.
*/
ProjectTag::ProjectTag(void )
{}


/**
	Constructor.

	@param		a_pElement			underlying XML element.
*/
ProjectTag::ProjectTag(TiXmlElement* a_pElement )
{
	copy(a_pElement );
}
		
		
/**
	Copy constructor.
	
	Required for STL containers.
*/
ProjectTag::ProjectTag(const ProjectTag& a_other )
{
	copy(a_other );
}
		
		
/**
	Destructor.
	
*/
ProjectTag::~ProjectTag()
{
	deleteTags();
}


/**
	Assignment operator.
	
	Required for STL containers.
*/
ProjectTag&	ProjectTag::operator=(const ProjectTag& a_other )
{
	copy(a_other );
	
	return	*this;
}


/**
	Copies an <code>XmlTag</code>.

	Used by copy constructor and assignment operator.
*/
void	ProjectTag::copy(TiXmlElement* a_pElement )
{
	m_strType				= a_pElement->ValueStr();
	m_iLineNumber			= a_pElement->Row();

	//MW:	Iterate through the attributes for this element...
	TiXmlAttribute*			pAttribute		= a_pElement->FirstAttribute();
	while	(pAttribute )
	{
		std::string			strName			= pAttribute->Name();
		std::string			strValue		= pAttribute->Value();
		m_mapAttributes[strName ]			= strValue;

		pAttribute			= pAttribute->Next();
	}

	storeNestedElements(a_pElement );
}

		
/**
	Copies an <code>XmlTag</code>.
	
	Used by copy constructor and assignment operator.
*/
void	ProjectTag::copy(const ProjectTag& a_other )
{
	//MW:	Only do the copy if this isn't a copy-into-self - a check here is
	//		important because there are both shallow & deep copies of structures
	//		happening - an unrestricted copy-into-self could zap some pointers...
	if (&a_other != this )
	{
		//MW:	Copy the simple objects...
		m_strType				= a_other.m_strType;
		m_iLineNumber			= a_other.m_iLineNumber;
		m_mapAttributes			= a_other.m_mapAttributes;

		//MW:	Remove any existing elements from the current list of tags...
		deleteTags();

		//MW:	Have to duplicate each individual pointer in the tag list...
		for (typeTagList::const_iterator iter = a_other.m_vectorTags.begin(); iter != a_other.m_vectorTags.end(); ++iter )
		{
			m_vectorTags.push_back((*iter )->clone() );
		}
	}
}
		
		
/**
	Indicates whether a given name is an acceptable type to be nested.
	
	By default, returns <code>true</code>. Derived classes may
	override to generate to strictly type nested elements. If this
	method returns <code>false</code> an error will be generated
	by the caller, indicating to the parser unit that there has been
	a tag error.
	
	@return		<code>true</code>.
*/
bool	ProjectTag::isAcceptableTag(const std::string& a_strTagType ) const
{
	return	true;
}
		
		
/**
	Gives the name of the tag.
	
	@return		tag name.
*/
std::string	ProjectTag::getType(void ) const
{
	return	m_strType;
}
		
		
/**
	Gives a value for a named attribute.

	@param		a_strName			name of attribute.
	@return		value of named attribute.
*/
std::string	ProjectTag::getAttribute(const std::string& a_strName )
{
	return m_mapAttributes[a_strName ];
}
		
		
/**
	Gives a map of all attributes.
	
	@return		map of attributes.
*/
ProjectTag::typeAttributeMap&	ProjectTag::getAttributes(void )
{
	return	m_mapAttributes;
}
		
		
/**
	Gives a list of all nested tags.
	
*/
ProjectTag::typeTagList&	ProjectTag::getTags(void )
{
	return	m_vectorTags;
}


/**
	Gives the line number of the containing tag.
	
*/
int	ProjectTag::getLineNumber(void ) const
{
	return	m_iLineNumber;
}


/**
	Indexer to give a value for a named attribute.
	
*/
std::string	ProjectTag::operator[](const std::string& a_strName )
{
	return	getAttribute(a_strName );
}


/**
	Removes all existing nested tags.
	
	Each <code>Tag</code> pointer is deleted and the list cleared.
*/
void	ProjectTag::deleteTags(void )
{
	if (m_vectorTags.size() > 0 )
	{
		for (typeTagList::iterator iter = m_vectorTags.begin(); iter != m_vectorTags.end(); ++iter )
		{
			delete (*iter);
		}
	
		m_vectorTags.clear();
	}
}


/**
	Builds a structure of nested tags.
	
*/
void	ProjectTag::storeNestedElements(TiXmlElement* a_pElement )
{
	//MW:	Start iterating at the first child element...
	TiXmlElement*			pCurrent		= static_cast<TiXmlElement*>(a_pElement->FirstChild() );
	while	(pCurrent )
	{
		if (pCurrent->Type() != TiXmlNode::COMMENT )
		{
			//MW:	Is this the name of an acceptable tag..?
			std::string			strName			= pCurrent->ValueStr();
			if (! isAcceptableTag(strName ) )
			{
				throw TagException("named tag '" + strName + "' cannot be nested within tag '" + getType() + "'", pCurrent->Row() );
			}

			//MW:	Attempt to create a specific object for this named type...
			Tag*				pTag			= TagFactory::getInstance().createTag(pCurrent, strName );
			if (! pTag )
			{
				throw TagException("unrecognised tag name '" + strName + "'", pCurrent->Row() );
			}
			
			//MW:	Store the pointer to the tag...
			m_vectorTags.push_back(pTag );
		}
		
		//MW:	Next element...
		pCurrent			= static_cast<TiXmlElement*>(a_pElement->IterateChildren(pCurrent ) );
	}
}



//--------------------------------------------------------------------------//



/**
	Constant.
	
*/
const std::string		ProjectInclude::STR_ATTRIB_PATH			= "path";
const std::string		ProjectInclude::STR_NAME_FILETAG		= "file";


/**
	Constructor.

*/
ProjectInclude::ProjectInclude(TiXmlElement* a_pElement )
{
	copyInclude(a_pElement );
}


/**
	Copy constructor.
	
	Required for STL containers.
*/
ProjectInclude::ProjectInclude(const ProjectInclude& a_other )
{
	copyInclude(a_other );
}


/**
	Assignment operator.
	
	Required for STL containers.
*/
ProjectInclude&	ProjectInclude::operator=(const ProjectInclude& a_other )
{
	copyInclude(a_other );
	
	return	*this;
}


/**
	Copies a <code>ProjectTag</code>.

	Used by copy constructor and assignment operator.
*/
void	ProjectInclude::copyInclude(TiXmlElement* a_pElement )
{
	//MW:	Copy the base parts...
	copy(a_pElement );
	
	//MW:	The 'include' specific parts...
	m_strPath				= getAttribute(STR_ATTRIB_PATH );
}


/**
	Copies an <code>ProjectTag</code>.

	Used by copy constructor and assignment operator.
*/
void	ProjectInclude::copyInclude(const ProjectInclude& a_other )
{
	//MW:	Copy the base parts...
	copy(a_other );
	
	//MW:	The 'include' specific parts...
	m_strPath				= a_other.m_strPath;
}
		
		
/**
	Gives the value of the <code>path</code> attribute.
	
*/
std::string	ProjectInclude::getPath(void )
{
	//MW:	By default use the value of the 'path' attribute - this will be
	//		overridden if one or more 'file' tags are specified...
	std::string				strPath;
	if (m_strPath.size() > 0 )
	{
		strPath				= m_strPath;
	}
	else
	{
		//MW:	Loop through the nested file tags...
		for (typeTagList::const_iterator iter = getTags().begin(); iter != getTags().end(); ++iter )
		{
			FileTag*		ptagFile		= static_cast<FileTag*>(*iter );
			std::string		strFilePath		= ptagFile->getPath();
			Log::write("ProjectInclude::getPath [file-path=" + strFilePath + "]" );
		}
	}
	
	return	strPath;
}

	
/**
	Indicates whether a named tag can be nested within this tag.
	
	Currently, only allows the <code>File</code> tag to be nested.
	
	@return		<code>true</code> if the named tag can nested,
				<code>false</code> otherwise.
*/
bool	ProjectInclude::isAcceptableTag(const std::string& a_strTagType ) const
{
	return	(a_strTagType == STR_NAME_FILETAG );
}



//--------------------------------------------------------------------------//



/**
	Constant.
	
*/
const std::string		ProjectProperty::STR_ATTRIB_NAME			= "name";
const std::string		ProjectProperty::STR_ATTRIB_VALUE			= "value";


/**
	Default constructor.
	
	Required for STL containers.
*/
ProjectProperty::ProjectProperty(void )
{}


/**
	Constructor.

*/
ProjectProperty::ProjectProperty(TiXmlElement* a_pElement )
{
	copyProperty(a_pElement );
}


/**
	Copy constructor.
	
	Required for STL containers.
*/
ProjectProperty::ProjectProperty(const ProjectProperty& a_other )
{
	copyProperty(a_other );
}


/**
	Assignment operator.
	
	Required for STL containers.
*/
ProjectProperty&	ProjectProperty::operator=(const ProjectProperty& a_other )
{
	copyProperty(a_other );
	
	return	*this;
}
		
		
/**
	Copies a <code>ProjectTag</code>.
	
	Used by copy constructor and assignment operator.
*/
void	ProjectProperty::copyProperty(TiXmlElement* a_pElement )
{
	//MW:	Copy the base parts...
	copy(a_pElement );
	
	//MW:	Property specific parts...
	m_strName				= getAttribute(STR_ATTRIB_NAME );
	m_strValue				= getAttribute(STR_ATTRIB_VALUE );
}


/**
	Copies an <code>ProjectTag</code>.
	
	Used by copy constructor and assignment operator.
*/
void	ProjectProperty::copyProperty(const ProjectProperty& a_other )
{
	//MW:	Copy the base parts...
	copy(a_other );
	
	//MW:	Property specific parts...
	m_strName				= a_other.m_strName;
	m_strValue				= a_other.m_strValue;
}


/**
	Gives the property name.
	
*/
std::string	ProjectProperty::getName(void ) const
{
	return	m_strName;
}


/**
	Gives the property value.
	
*/
std::string	ProjectProperty::getValue(void ) const
{
	return	m_strValue;
}



//--------------------------------------------------------------------------//



/**
	Constant.
	
*/
const std::string		ProjectTarget::STR_ATTRIB_NAME			= "name";
const std::string		ProjectTarget::STR_ATTRIB_DEPENDS		= "depends";
const std::string		ProjectTarget::STR_ATTRIB_DESCRIPTION	= "description";


/**
	Default constructor.
	
	Required for STL containers.
*/
ProjectTarget::ProjectTarget(void )
{}


/**
	Constructor.

*/
ProjectTarget::ProjectTarget(TiXmlElement* a_pElement )
{
	copyTarget(a_pElement );
}


/**
	Copy constructor.
	
	Required for STL containers.
*/
ProjectTarget::ProjectTarget(const ProjectTarget& a_other )
{
	copyTarget(a_other );
}


/**
	Assignment operator.
	
	Required for STL containers.
*/
ProjectTarget&	ProjectTarget::operator=(const ProjectTarget& a_other )
{
	copyTarget(a_other );
	
	return	*this;
}


/**
	Copies a <code>ProjectTag</code>.
	
	Used by copy constructor and assignment operator.
*/
void	ProjectTarget::copyTarget(TiXmlElement* a_pElement )
{
	//MW:	Copy the base parts...
	copy(a_pElement );

	//MW:	Target specific parts...
	m_strName				= getAttribute(STR_ATTRIB_NAME );
	m_strDescription		= getAttribute(STR_ATTRIB_DESCRIPTION );
	
	m_depends.copy(getAttribute(STR_ATTRIB_DEPENDS ) );
}


/**
	Copies an <code>ProjectTag</code>.
	
	Used by copy constructor and assignment operator.
*/
void	ProjectTarget::copyTarget(const ProjectTarget& a_other )
{
	//MW:	Copy the base parts...
	copy(a_other );
	
	//MW:	Target specific parts...
	m_strName				= a_other.m_strName;
	m_strDescription		= a_other.m_strDescription;
	m_depends				= a_other.m_depends;
}


/**
	Gives the target name.
	
*/
std::string	ProjectTarget::getName(void ) const
{
	return	m_strName;
}


/**
	Gives the target dependencies.
	
*/
Dependency&	ProjectTarget::getDepends(void )
{
	return	m_depends;
}


/**
	Gives the target description
	
*/
std::string	ProjectTarget::getDescription(void ) const
{
	return	m_strDescription;
}
		
		
/**
	Executes this tag.
	
	@param		a_project			invoking project.
*/
void	ProjectTarget::execute(Project& a_project )
{
	Dependency				dependencies;
	
	execute(a_project, dependencies );
}


/**
	Internal <code>execute</code> method.
	
	A <code>Dependency</code> object is passed as an argument. This
	allows a running list of called targets to be maintained. In this
	way, no target will be called more than once, no matter how many
	times it appears in the dependency chain.
	
	@param		a_project			invoking project.
	@param		a_dependencies		list of dependencies called.
*/
void	ProjectTarget::execute(Project& a_project, Dependency& a_dependencies )
{
	//MW:	Only execute this target if it hasn't been execute already...
	std::string				strName				= getName();
	if (! a_dependencies.contains(strName ) )
	{
		//MW:	Adding this name to the dependencies prevents it being invoked
		//		again...
		a_dependencies.add(strName );
		
		//MW:	First execute any dependencies...
		Dependency::typeDependencyList			depends				= getDepends().getDependencies();
		for (Dependency::typeDependencyList::const_iterator iterDepends = depends.begin();
				iterDepends != depends.end(); ++iterDepends )
		{
			std::string			strDependency			= *iterDepends;
			a_project.getTarget(strDependency ).execute(a_project, a_dependencies );
		}

		//MW:	Loop through the nested tags - executing each one...
		for (typeTagList::iterator iter = getTags().begin(); iter != getTags().end(); ++iter )
		{
			(*iter)->execute(a_project, *this );
		}
	}
}



//--------------------------------------------------------------------------//



/**
	Default constructor.
	
	Required for STL containers.
*/
Tag::Tag(void )
{}


/**
	Constructor.

*/
Tag::Tag(TiXmlElement* a_element )
{
	copy(a_element );
}


/**
	Copy constructor.
	
	Required for STL containers.
*/
Tag::Tag(const Tag& a_other )
{
	copy(a_other );
}


/**
	Assignment operator.
	
	Required for STL containers.
*/
Tag&	Tag::operator=(const Tag& a_other )
{
	copy(a_other );
	
	return	*this;
}


/**
	Destructor.
	
*/
Tag::~Tag()
{}



//--------------------------------------------------------------------------//



/**
	Destructor.
	
*/
ITagCreator::~ITagCreator()
{}



//--------------------------------------------------------------------------//



/**
	Hidden constructor.
	
*/
TagFactory::TagFactory(void )
{}


/**
	Destructor.
	
*/
TagFactory::~TagFactory()
{}


/**
	Registers an <code>ITagCreator</code> object.
	
	@param		a_creator		object to be registered.
*/
void	TagFactory::registerCreator(ITagCreator* a_pcreator )
{
	m_vectorCreators.push_back(a_pcreator );
}


/**
	Creates a typed <code>Tag</code> object.
	
	Implementation for the <code>ITagCreator</code> interface.
	
	@param		a_pElement		xml element.
	@param		a_strName		name of required tag.
	@return		typed <code>Tag</code> object if name is recognised,
				<code>null</code> otherwise.
*/
Tag*	TagFactory::createTag(TiXmlElement* a_pElement, const std::string& a_strName )
{
	Tag*				tagResult				= 0x0;
	
	for (typeCreatorList::iterator iter = m_vectorCreators.begin(); iter != m_vectorCreators.end(); ++iter )
	{
		tagResult		= (*iter)->createTag(a_pElement, a_strName );
		if (tagResult )
		{
			break;
		}
	}
	
	return	tagResult;
}
